#include "i2c_soft.h"
#include "systick.h"

void i2c_sda_out(void)
{
    gpio_mode_set(I2C_SDA_PORN, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, I2C_SDA_PIN);
    gpio_output_options_set(I2C_SDA_PORN, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SDA_PIN);
}

void i2c_sda_in(void)
{
    gpio_mode_set(I2C_SDA_PORN, GPIO_MODE_INPUT, GPIO_PUPD_NONE, I2C_SDA_PIN);
}

void i2c_soft_config(void)
{
    // scl pb6 开漏
    // sda pb7 开漏
    rcu_periph_clock_enable(I2C_RCU_PORN);
    //
    gpio_mode_set(I2C_SCL_PORN, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, I2C_SCL_PIN);
    gpio_output_options_set(I2C_SCL_PORN, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SCL_PIN);

    i2c_sda_out();

    // 空闲状态下为高电平
    // SCL(1);
    // SDA(1);
}

void i2c_soft_start(void)
{
    SCL(0);
    I2C_DELAY();
    SDA(1);
    I2C_DELAY();
    SCL(1);
    I2C_DELAY();
    SDA(0);
    I2C_DELAY();
    SCL(0);
}

void i2c_soft_stop(void)
{
    SCL(0);
    I2C_DELAY();
    SDA(0);
    I2C_DELAY();
    SCL(1);
    I2C_DELAY();
    SDA(1);
    I2C_DELAY();
    SCL(0);
}

void i2c_soft_send_bit(uint8_t bit)
{
    SCL(0);
    I2C_DELAY();
    SDA(bit);
    I2C_DELAY();
    SCL(1);
    I2C_DELAY();
    SCL(0);
}

void i2c_soft_send(uint8_t data)
{
    int i;
    uint8_t bit;
    for (i = 0; i < 8; i++)
    {
        bit = (data >> (7 - i)) & 0b1U;
        // printf("[i2c_soft_send]:%#x,[%d]= %#x\n", data, i, bit);
        i2c_soft_send_bit(bit);
    }
}

uint8_t i2c_soft_wait_ack(void)
{
    SCL(0);
    i2c_sda_in();
    I2C_DELAY();
    SCL(1);
    I2C_DELAY();
    uint8_t ack = gpio_input_bit_get(I2C_SDA_PORN, I2C_SDA_PIN);
    SCL(0);
    I2C_DELAY();
    i2c_sda_out();
    return ack;
}

uint8_t i2c_soft_read_byte()
{
    SCL(0);
    I2C_DELAY();
    i2c_sda_in();
    //
    uint8_t data = 0;
    for (size_t i = 0; i < 8; i++)
    {
        SCL(0);
        I2C_DELAY();
        SCL(1);
        data |= (gpio_input_bit_get(I2C_SDA_PORN, I2C_SDA_PIN) << (7 - i));
        I2C_DELAY();
    }
    //
    SCL(0);
    I2C_DELAY();
    i2c_sda_out();
    return data;
}

uint8_t i2c_soft_ack(void)
{
    SCL(0);
    I2C_DELAY();
    i2c_sda_out();
    SDA(0);
    I2C_DELAY();
    SCL(1);
    I2C_DELAY();
    SCL(0);
    I2C_DELAY();
}

uint8_t i2c_soft_nack(void)
{
    SCL(0);
    I2C_DELAY();
    i2c_sda_out();
    SDA(1);
    I2C_DELAY();
    SCL(1);
    I2C_DELAY();
    SCL(0);
    I2C_DELAY();
}

uint8_t i2c_soft_write(uint8_t addr, uint8_t rega, uint8_t *data, uint32_t len)
{
    i2c_soft_start();

    // 发送从设备地址
    i2c_soft_send(addr << 1);
    // 等待从设备ack
    if (i2c_soft_wait_ack())
    {
        return 1;
    }

    // 发送从设备寄存器地址
    i2c_soft_send(rega);
    if (i2c_soft_wait_ack())
    {
        return 2;
    }

    // 发送数据
    size_t i;
    for (i = 0; i < len; i++)
    {
        i2c_soft_send(data[i]);
        if (i2c_soft_wait_ack())
        {
            return 3;
        }
    }
    //
    i2c_soft_stop();

    return 0;
}

uint8_t i2c_soft_send_addr(uint8_t addr)
{
    i2c_soft_send(addr);
    if (i2c_soft_wait_ack())
    {
        return 3;
    }

    return 0;
}

uint8_t i2c_soft_send_register(uint8_t register_address)
{
    i2c_soft_send(register_address);
    if (i2c_soft_wait_ack())
    {
        return 4;
    }

    return 0;
}

uint8_t i2c_soft_read(uint8_t addr, uint8_t rega, uint8_t *data, uint32_t len)
{
    uint8_t rst = 0;
    // 开始信号
    i2c_soft_start();
    // 发送从设备地址,并等待响应
    //printf("[i2c_soft_read]:addr=%#x,rega=%#x\n", addr, rega);
    //
    rst = i2c_soft_send_addr(addr << 1);
    if (rst)
        return rst;
    // 发送寄存器地址,并等待
    rst = i2c_soft_send_register(rega);
    if (rst)
        return rst;

    // 再开始
    i2c_soft_start();
    // 发送读地址,并等待响应
    rst = i2c_soft_send_addr((addr << 1) | 0x1);
    if (rst)
        return rst;

    //

    // 接收数据,并发送响应

    for (int i = 0; i < len; i++)
    {
        *(data + i) = i2c_soft_read_byte();

        if (i < (len - 1))
        {
            i2c_soft_ack();
        }
        else
        {
            i2c_soft_nack();
        }
    }

    // 停止
    i2c_soft_stop();
    return rst;
}

void i2c_soft_test(void)
{
}
